﻿using System;
using System.Collections.Generic;
using System.Globalization;

namespace Needle.Utils
{
    /// <summary>
    /// 
    /// </summary>
    public class NaturalComparer : IComparer<String>
    {

        private CultureInfo _culture;
        private bool _ignoreCase;
        /// <summary>
        /// 
        /// </summary>
        public NaturalComparer()
        {
            _culture = CultureInfo.CurrentUICulture;
            _ignoreCase = false;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="ignoreCase"></param>
        public NaturalComparer(bool ignoreCase)
        {
            _culture = CultureInfo.CurrentUICulture;
            _ignoreCase = ignoreCase;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="ignoreCase"></param>
        /// <param name="culture"></param>
        public NaturalComparer(bool ignoreCase, CultureInfo culture)
        {
            Ensure.NotNull(culture, "culture");
            _culture = culture;
            _ignoreCase = ignoreCase;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public int Compare(string x, string y)
        {
            if (string.ReferenceEquals(x, y)  )
                return 0;

            if (x == null)
                return y == null ? 0 : -1;

            if (y == null)
                return 1;

            int xLength, yLength; 
            xLength = x.Length; yLength = y.Length;

            if (xLength == 0 || yLength == 0)
                return xLength - yLength;

            int xIndex, yIndex;  
            xIndex = 0; yIndex = 0;
            int xCount, yCount;
            bool isDigit;
            int result = 0;
            while (true)
            {
                isDigit = Char.IsDigit(x[xIndex]);

                if (Char.IsDigit(y[yIndex]) ^ isDigit)
                    return String.Compare(x, xIndex, y, yIndex, 1, _ignoreCase, _culture); // this can never return 0

                //count number of same type of chars in x
                for (xCount = 0; (xIndex + xCount < xLength) && Char.IsDigit(x, xIndex + xCount) == isDigit; xCount++) { }

                //count number of same type of chars in y
                for (yCount = 0; (yIndex + yCount < yLength) && Char.IsDigit(y, yIndex + yCount) == isDigit; yCount++) { }

                if (isDigit)
                {
                    string yPart = y.Substring(yIndex, yCount);
                    string xPart = x.Substring(xIndex, xCount);
                    result = Math.Sign(double.Parse(xPart, _culture.NumberFormat) - double.Parse(yPart, _culture.NumberFormat));
                }
                else
                {
                    result = String.Compare(x, xIndex, y, yIndex, Math.Min(xCount, yCount), _ignoreCase, _culture);
                    if (result == 0)
                        result = xCount - yCount;
                }

                if (result != 0)
                    return result;

                xIndex += xCount;
                yIndex += yCount;

                if (xIndex < xLength && yIndex < yLength) continue;
                
                return result;
            }

        }

    }

}
